﻿using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http.Headers;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;

namespace MarkDownFileSplitter.Meilisearch
{
    internal class MeilisearchUploader
    {
        private readonly HttpClient _httpClient;
        private string _apiKey;
        private string _baseUrl;

        private static MeilisearchUploader _instance;

        private MeilisearchUploader(string apiKey, string baseUrl)
        {
            _apiKey = apiKey;
            _baseUrl = baseUrl.TrimEnd('/');
            _httpClient = new HttpClient();
            _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _apiKey);
        }

        public static MeilisearchUploader Instance
        {
            get
            {
                if (_instance == null)
                {
                    //_instance = new MeilisearchUploader("4eaf6543-6953-44ff-b2a7-48f966e64bba", "http://192.168.2.203:7700");
                    _instance = new MeilisearchUploader(GlobalSetting.Instance.MeilisearchAPIKey, GlobalSetting.Instance.MeilisearchUrl);
                }
                return _instance;
            }
        }

        public void UpdateSetting()
        {
            _apiKey = GlobalSetting.Instance.MeilisearchAPIKey;
            _baseUrl = GlobalSetting.Instance.MeilisearchUrl;
            _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _apiKey);
        }

        #region Index
        /// <summary>
        /// 创建新的Index
        /// </summary>
        /// <param name="indexUid"></param>
        /// <param name="primaryKey"></param>
        /// <returns></returns>
        public async Task<JObject> CreateIndex(string indexUid, string primaryKey)
        {
            var requestBody = new JObject
            {
                ["uid"] = indexUid,
                ["primaryKey"] = primaryKey,
            };
            var response = await _httpClient.PostAsync($"{_baseUrl}/indexes", new StringContent(requestBody.ToString(), Encoding.UTF8, "application/json"));
            response.EnsureSuccessStatusCode();
            return JObject.Parse(await response.Content.ReadAsStringAsync());
        }
        /// <summary>
        /// 获取所有index
        /// </summary>
        /// <returns></returns>
        public async Task<JObject> QueryIndexes()
        {
            try
            {
                var response = await _httpClient.GetAsync($"{_baseUrl}/indexes");
                response.EnsureSuccessStatusCode();
                return JObject.Parse(await response.Content.ReadAsStringAsync());
            }
            catch (Exception ex)
            {
                return null;
            }
        }
        /// <summary>
        /// 删除index
        /// </summary>
        /// <param name="indexUid"></param>
        /// <returns></returns>
        public async Task<JObject> DeleteIndex(string indexUid)
        {
            var response = await _httpClient.DeleteAsync($"{_baseUrl}/indexes/{indexUid}");
            response.EnsureSuccessStatusCode();
            return JObject.Parse(await response.Content.ReadAsStringAsync());
        }
        #endregion

        #region Document
        public async Task<JObject> AddOrUpdateDocuments(JArray documents, string indexUid)
        {
            var response = await _httpClient.PutAsync($"{_baseUrl}/indexes/{indexUid}/documents", new StringContent(documents.ToString(), Encoding.UTF8, "application/json"));
            response.EnsureSuccessStatusCode();
            return JObject.Parse(await response.Content.ReadAsStringAsync());
        }
        /// <summary>
        /// 清空index的所有document
        /// </summary>
        /// <param name="indexUid"></param>
        /// <returns></returns>
        public async Task<JObject> ClearDocuments(string indexUid)
        {
            var response = await _httpClient.DeleteAsync($"{_baseUrl}/indexes/{indexUid}/documents");
            response.EnsureSuccessStatusCode();
            return JObject.Parse(await response.Content.ReadAsStringAsync());
        }
        public async Task<List<string>> GetDocumentAttributeNames(string indexUid)
        {
            var documents = await GetDocuments(indexUid, 1);
            // 获取第一个文档对象
            var firstDocument = documents.FirstOrDefault() as JObject;
            if (firstDocument == null)
            {
                return new List<string>();
            }

            return firstDocument.Properties().Select(p => p.Name).ToList();
        }
        public async Task<JArray> GetDocuments(string indexUid, int limit)
        {
            var response = await _httpClient.GetAsync($"{_baseUrl}/indexes/{indexUid}/documents?limit={limit}");
            response.EnsureSuccessStatusCode();
            var result = JObject.Parse(await response.Content.ReadAsStringAsync());
            return result["results"] as JArray;
        }
        /// <summary>
        /// https://www.meilisearch.com/docs/reference/api/documents#delete-one-document
        /// DELETE /indexes/{index_uid    }/documents/{document_id}
        /// Delete one document based on its unique id.
        /// Path parameters
        ///     Name Type    Description
        ///     index_uid *  String  uid of the requested index
        ///     document_id * String/Integer Document id of the requested document
        /// curl \
        ///   -X DELETE 'MEILISEARCH_URL/indexes/movies/documents/25684'
        /// Response: 202 Accepted
        ///     {
        ///         "taskUid": 1,
        ///         "indexUid": "movies",
        ///         "status": "enqueued",
        ///         "type": "documentDeletion",
        ///         "enqueuedAt": "2021-08-11T09:25:53.000000Z"
        ///     }
        /// </summary>
        /// <returns></returns>
        public async Task<bool> DeleteOneDocument(string indexUid, string documentId)
        {
            try
            {
                var response = await _httpClient.DeleteAsync($"{_baseUrl}/indexes/{indexUid}/documents/{documentId}");
                // 检查响应状态码是否为 202 Accepted
                if (response.StatusCode == System.Net.HttpStatusCode.Accepted)
                {
                    return true;
                }
                else
                {
                    // 记录错误信息，可根据实际情况添加日志记录逻辑
                    return false;
                }
            }
            catch (Exception ex)
            {
                // 记录异常信息，可根据实际情况添加日志记录逻辑
                return false;
            }
        }
        #endregion

        #region Filter
        public async Task<JObject> UpdateFilterSetting(string indexUid, List<FilterAttribute> filterAttributes)
        {
            // 创建过滤配置对象的辅助方法
            JObject CreateFilterConfigObject(List<string> attributes, bool enableEquality, bool enableComparison)
            {
                return new JObject
                {
                    ["attributePatterns"] = new JArray(attributes),
                    ["features"] = new JObject
                    {
                        ["facetSearch"] = false,
                        ["filter"] = new JObject
                        {
                            ["equality"] = enableEquality,
                            ["comparison"] = enableComparison
                        }
                    }
                };
            }

            // 按EnableEquality和EnableComparison的组合分为四种类型
            List<string> equalityAndComparisonFilters = filterAttributes.Where(x => x.EnableEquality && x.EnableComparison).Select(x => x.AttributeName).ToList();
            List<string> equalityOnlyFilters = filterAttributes.Where(x => x.EnableEquality && !x.EnableComparison).Select(x => x.AttributeName).ToList();
            List<string> comparisonOnlyFilters = filterAttributes.Where(x => !x.EnableEquality && x.EnableComparison).Select(x => x.AttributeName).ToList();
            var requestBody = new JArray();
            // 添加同时支持相等和比较过滤的属性
            if (equalityAndComparisonFilters.Any())
            {
                requestBody.Add(CreateFilterConfigObject(equalityAndComparisonFilters, true, true));
            }
            // 添加仅支持相等过滤的属性
            if (equalityOnlyFilters.Any())
            {
                requestBody.Add(CreateFilterConfigObject(equalityOnlyFilters, true, false));
            }
            // 添加仅支持比较过滤的属性
            if (comparisonOnlyFilters.Any())
            {
                requestBody.Add(CreateFilterConfigObject(comparisonOnlyFilters, false, true));
            }

            var response = await _httpClient.PutAsync($"{_baseUrl}/indexes/{indexUid}/settings/filterable-attributes", new StringContent(requestBody.ToString(), Encoding.UTF8, "application/json"));
            response.EnsureSuccessStatusCode();
            return JObject.Parse(await response.Content.ReadAsStringAsync());
        }
        #region Settings
        /// <summary>
        /// 获取索引设置
        /// </summary>
        /// <param name="indexUid">索引名称</param>
        /// <returns>索引设置</returns>
        public async Task<JObject> GetIndexSettings(string indexUid)
        {
            var response = await _httpClient.GetAsync($"{_baseUrl}/indexes/{indexUid}/settings");
            response.EnsureSuccessStatusCode();
            return JObject.Parse(await response.Content.ReadAsStringAsync());
        }

        /// <summary>
        /// 更新索引设置
        /// </summary>
        /// <param name="indexUid">索引名称</param>
        /// <param name="settings">设置对象</param>
        /// <returns>任务信息</returns>
        public async Task<JObject> UpdateIndexSettings(string indexUid, JObject settings)
        {
            var response = await _httpClient.PatchAsync($"{_baseUrl}/indexes/{indexUid}/settings", 
                new StringContent(settings.ToString(), Encoding.UTF8, "application/json"));
            response.EnsureSuccessStatusCode();
            return JObject.Parse(await response.Content.ReadAsStringAsync());
        }
        #endregion
        /// <summary>
        /// 获得过滤条件的字段
        /// </summary>
        /// <param name="indexUid"></param>
        /// <returns></returns>
        public async Task<JArray> GetSettingOfFilterableAttributes(string indexUid)
        {
            var response = await _httpClient.GetAsync($"{_baseUrl}/indexes/{indexUid}/settings/filterable-attributes");
            response.EnsureSuccessStatusCode();
            return JArray.Parse(await response.Content.ReadAsStringAsync());
        }
        /// <summary>
        /// 获得可搜索字段的设置
        /// </summary>
        /// <param name="indexUid"></param>
        /// <returns></returns>
        public async Task<JArray> GetSettingOfSearchableAttributes(string indexUid)
        {
            var response = await _httpClient.GetAsync($"{_baseUrl}/indexes/{indexUid}/settings/filterable-attributes");
            response.EnsureSuccessStatusCode();
            return JArray.Parse(await response.Content.ReadAsStringAsync());
        }
        #endregion

        #region Task
        public async Task<JArray> GetTasksByIndexUid(string indexUid)
        {
            var response = await _httpClient.GetAsync($"{_baseUrl}/tasks/?indexUids={indexUid}");
            response.EnsureSuccessStatusCode();
            var result = JObject.Parse(await response.Content.ReadAsStringAsync());
            return (JArray)result["results"];
        }

        /// <summary>
        /// 轮询Task是否成功
        /// </summary>
        /// <param name="taskUid"></param>
        /// <returns></returns>
        public async Task<bool> IsTaskSuccess(string taskUid, int timeSpan = 500, bool silent = false)
        {
            while (true)
            {
                var taskStatus = await GetTaskStatus(taskUid);
                string status = taskStatus["status"]?.ToString();
                if (status == "succeeded")
                {
                    if (!silent)
                        MessageBox.Show("任务执行成功");
                    return true;
                }
                else if (status == "failed")
                {
                    if (!silent)
                        MessageBox.Show("任务执行失败", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return false;
                }
                else if (status == "canceled")
                {
                    if (!silent)
                        MessageBox.Show("任务已被取消", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                    return false;
                }
                // 等待指定时间后继续查询
                await Task.Delay(timeSpan);
            }
        }

        /// <summary>
        /// 获取Task状态信息
        /// </summary>
        /// <param name="taskUid"></param>
        /// <returns></returns>
        private async Task<JObject> GetTaskStatus(string taskUid)
        {
            var response = await _httpClient.GetAsync($"{_baseUrl}/tasks/{taskUid}");
            response.EnsureSuccessStatusCode();
            return JObject.Parse(await response.Content.ReadAsStringAsync());
        }
        #endregion

        #region Search
        public async Task<JObject> Search(string indexUid, string query, int limit, int offset,string filter)
        {
            var requestBody = new JObject
            {
                ["q"] = query,
                ["limit"] = limit,
                ["offset"] = offset,
                ["showRankingScore"]=true,
                ["showRankingScoreDetails"] = true,
                ["filter"]= filter,
            };
            var response = await _httpClient.PostAsync($"{_baseUrl}/indexes/{indexUid}/search", new StringContent(requestBody.ToString(), Encoding.UTF8, "application/json"));
            response.EnsureSuccessStatusCode();
            return JObject.Parse(await response.Content.ReadAsStringAsync());
        }
        #endregion
 		#region PrimaryKey
        public async Task<JObject> UpdatePrimaryKey(string indexUid,string primaryKey)
        {
            var requestBody = new JObject
            {
                ["primaryKey"] = primaryKey,
            };
            var response = await _httpClient.PatchAsync($"{_baseUrl}/indexes/{indexUid}",new StringContent(requestBody.ToString(), Encoding.UTF8, "application/json"));
            response.EnsureSuccessStatusCode();
            return JObject.Parse(await response.Content.ReadAsStringAsync());
        }
        #endregion
        #region Document
        /// <summary>
        /// 获取索引中ID字段的最大值
        /// </summary>
        /// <param name="indexUid">索引名称</param>
        /// <returns>最大ID值，如果没有文档则返回0</returns>
        public async Task<int> GetMaxIdValue(string indexUid)
        {
            try
            {
                // 获取所有文档，按ID降序排列，只取第一个
                var response = await _httpClient.PostAsync($"{_baseUrl}/indexes/{indexUid}/search", 
                    new StringContent(new JObject
                    {
                        ["q"] = "",
                        ["limit"] = 1,
                        ["sort"] = new JArray { "Id:desc" }
                    }.ToString(), Encoding.UTF8, "application/json"));
                
                if (response.IsSuccessStatusCode)
                {
                    var result = JObject.Parse(await response.Content.ReadAsStringAsync());
                    var hits = result["hits"] as JArray;
                    
                    if (hits != null && hits.Count > 0)
                    {
                        var firstDoc = hits[0] as JObject;
                        var idValue = firstDoc?["Id"]?.ToString();
                        
                        if (int.TryParse(idValue, out int maxId))
                        {
                            return maxId;
                        }
                    }
                }
                
                return 0;
            }
            catch
            {
                return 0;
            }
        }
        #endregion
    }
}
